package cn.mujiankeji.mbrowser.功能.下载

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import cn.mujiankeji.mbrowser.R // 假设 R 文件路径，用于图标
import cn.mujiankeji.mbrowser.功能.下载.引擎.默认下载引擎 // 直接使用引擎示例
import cn.mujiankeji.mbrowser.功能.下载.引擎.M3u8DownloadEngine // 使用正确的类名
import cn.mujiankeji.mbrowser.功能.下载.工具.M3U8解析器 // 导入M3U8解析器
import cn.mujiankeji.mbrowser.功能.下载.模型.下载引擎类型
import cn.mujiankeji.mbrowser.功能.下载.模型.下载任务
import cn.mujiankeji.mbrowser.功能.下载.模型.下载状态
import cn.mujiankeji.mbrowser.功能.下载.仓库.下载仓库接口
import io.ktor.client.HttpClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.CancellationException

class 下载服务 : Service(), KoinComponent {

    private val TAG = "下载服务"
    private val NOTIFICATION_CHANNEL_ID = "下载服务通道"
    private val NOTIFICATION_ID_OFFSET = 1000 // 避免与其它通知ID冲突

    // 依赖注入
    private val 下载管理器: 下载管理器接口 by inject()
    private val 下载仓库: 下载仓库接口 by inject() // 下载管理器会用，但服务内部也可能直接用
    private val httpClient: HttpClient by inject() // 用于创建引擎

    private val serviceJob = SupervisorJob()
    private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)

    // 管理活跃的下载Job，key为任务ID，value为对应的Job
    private val activeDownloadJobs = ConcurrentHashMap<String, Job>()
    private val notificationIdCounter = AtomicInteger(NOTIFICATION_ID_OFFSET)

    // 下载任务队列，用于接收通过ID启动的下载请求
    private val taskProcessingChannel = Channel<String>(Channel.UNLIMITED) // 改为接收任务ID

    private val binder = 下载服务Binder()

    inner class 下载服务Binder : Binder() {
        fun getService(): 下载服务 = this@下载服务
    }

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "创建下载服务")
        createNotificationChannel()

        // 启动任务处理器，处理来自channel的任务ID
        serviceScope.launch {
            for (taskId in taskProcessingChannel) {
                if (!isActive) break // 服务销毁时退出循环
                val task = 下载仓库.获取下载任务快照(taskId)
                if (task != null && task.状态 != 下载状态.正在下载 && task.状态 != 下载状态.已完成) {
                     // 检查是否已在处理 或达到最大并发数
                    if (activeDownloadJobs.size < MAX_CONCURRENT_DOWNLOADS && !activeDownloadJobs.containsKey(taskId)) {
                        Log.d(TAG, "准备启动任务: $taskId, 文件大小: ${formatBytes(task.文件大小)}, URL: ${task.url}")
                        val job = 开始下载(task) // 启动下载并获取Job
                        activeDownloadJobs[taskId] = job
                        Log.d(TAG, "任务 $taskId 已添加到活跃任务列表，当前活跃任务数: ${activeDownloadJobs.size}")
                        job.invokeOnCompletion { cause ->
                            Log.d(TAG, "任务 $taskId 完成，原因: ${cause?.message ?: "正常完成"}")
                            activeDownloadJobs.remove(taskId)
                            Log.d(TAG, "任务 $taskId 已从活跃任务列表移除，剩余活跃任务数: ${activeDownloadJobs.size}")
                        }
                    } else {
                        Log.i(TAG, "任务 $taskId 推迟执行，当前活跃任务数: ${activeDownloadJobs.size}")
                        // 可以将任务状态更新为等待中，或稍后重新加入队列
                        下载仓库.更新下载任务(task.copy(状态 = 下载状态.等待中))
                    }
                } else if (task == null) {
                    Log.w(TAG, "任务处理器无法找到任务: $taskId")
                } else {
                    Log.i(TAG, "任务 $taskId (${task.状态}) 无需通过channel处理")
                }
            }
            Log.d(TAG, "任务处理器协程已退出")
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "收到服务命令: $intent")
        intent?.let {
            val taskId = it.getStringExtra(EXTRA_TASK_ID)
            when (it.action) {
                开始下载 -> {
                    taskId?.let { id ->
                        Log.d(TAG, "接收到通过ID启动任务的请求: $id")
                        serviceScope.launch { 
                            Log.d(TAG, "将任务 $id 发送到处理队列")
                            taskProcessingChannel.send(id) 
                        }
                    }
                }
                暂停下载 -> {
                    taskId?.let { id -> 暂停指定任务(id) }
                }
                恢复下载 -> {
                    taskId?.let { id ->
                         Log.d(TAG, "接收到通过ID恢复任务的请求: $id")
                         serviceScope.launch { 
                             Log.d(TAG, "将恢复任务 $id 发送到处理队列")
                             taskProcessingChannel.send(id) 
                         } // 恢复也通过channel处理
                    }
                }
                取消下载 -> {
                    taskId?.let { id -> 取消指定任务(id) }
                }
                else ->{
                    Log.d(TAG, "收到未知操作: ${it.action}")
                    // TODO: Add other actions like pause all, resume all, etc.
                }

            }
        }
        return START_NOT_STICKY
    }

    private fun 开始下载(任务: 下载任务): Job {
       return serviceScope.launch {
            var currentTask = 任务.copy()
            val notificationId = notificationIdCounter.getAndIncrement()

            try {
                // 1. 更新任务状态为正在下载，并存入仓库
                currentTask = currentTask.copy(状态 = 下载状态.正在下载)
                下载仓库.更新下载任务(currentTask)
                startForeground(notificationId, buildNotification(currentTask, "正在准备下载...").build())


                // 2. 根据任务类型选择合适的下载引擎
                val engine = when (currentTask.引擎类型) {
                    下载引擎类型.KTOR -> {
                        默认下载引擎(httpClient)
                    }
                    下载引擎类型.KTOR_M3U8 -> {
                        val m3u8解析器 = M3U8解析器() // 创建新的M3U8解析器实例
                        M3u8DownloadEngine(httpClient, m3u8解析器)
                    }
                    else -> {
                        throw IllegalArgumentException("不支持的引擎类型: ${currentTask.引擎类型}")
                    }
                }

                // 3. 调用引擎的开始下载方法，并收集Flow以更新进度和通知
                var lastLogTime = System.currentTimeMillis()
                var lastDownloadedSize = 0L
                
                engine.开始下载(currentTask).collectLatest { updatedTask ->
                    currentTask = updatedTask // 保持 currentTask 是最新的
                    serviceScope.launch {
                        下载仓库.更新下载任务(currentTask)
                    }
                    
                    if (isActive) { // 确保服务和协程仍然活跃
                         when (currentTask.状态) {
                            下载状态.正在下载 -> {
                                updateNotification(notificationId, currentTask, "下载中")
                            }
                            下载状态.已完成 -> {
                                updateNotification(notificationId, currentTask, "下载完成")
                                stopForeground(false) // 完成了单个任务，但服务可能仍在运行其他任务
                                // 如果不保留通知，则取消： getSystemService(NotificationManager::class.java).cancel(notificationId)
                                triggerNextTaskInQueue() // 尝试启动下一个排队任务
                            }
                            下载状态.失败 -> {
                                updateNotification(notificationId, currentTask, "下载失败")
                                stopForeground(false)
                                triggerNextTaskInQueue()
                            }
                            下载状态.已取消 -> {
                                updateNotification(notificationId, currentTask, "已取消")
                                stopForeground(false) 
                                // 清理文件等操作应在引擎或管理器中处理
                                triggerNextTaskInQueue()
                            }
                            下载状态.已暂停 -> {
                                updateNotification(notificationId, currentTask, "已暂停")
                                stopForeground(false)
                                // 不需要 triggerNextTaskInQueue，因为暂停是主动行为
                            }
                            else -> {
                                Log.d(TAG, "任务 ${currentTask.id} 状态更新为: ${currentTask.状态}")
                            }
                        }
                    }
                }
                Log.d(TAG, "任务 ${currentTask.id} 的Flow收集已结束，最终状态: ${currentTask.状态}")
            } catch (e: Exception) {
                if (isActive) {
                    Log.e(TAG, "处理任务 ${currentTask.id} 时发生异常", e)
                    val finalStatus = if (e is kotlinx.coroutines.CancellationException) 下载状态.已取消 else 下载状态.失败
                    currentTask = currentTask.copy(状态 = finalStatus, 错误信息 = e.message ?: "未知错误")
                    下载仓库.更新下载任务(currentTask)
                    updateNotification(notificationId, currentTask, if (finalStatus == 下载状态.已取消) "已取消" else "下载出错")
                    stopForeground(false)
                    triggerNextTaskInQueue()
                }
            } finally {
                 activeDownloadJobs.remove(currentTask.id) // 确保从活跃任务中移除
                 Log.d(TAG, "任务 ${currentTask.id} 处理完成，已从活跃任务列表移除")
                // 如果所有任务都完成了，并且没有挂起的intent，可以考虑stopSelf()
                if (activeDownloadJobs.isEmpty() && !taskProcessingChannel.tryReceive().isSuccess) {
                     Log.i(TAG, "所有任务处理完毕，服务准备停止")
                     // stopSelf() // 谨慎使用，确保确实没有任务了
                }
            }
        }
    }

    private fun 暂停指定任务(任务Id: String) {
        Log.d(TAG, "请求暂停任务: $任务Id")
        activeDownloadJobs[任务Id]?.let { job ->
            Log.d(TAG, "找到任务 $任务Id 的Job，准备取消")
            job.cancel(kotlinx.coroutines.CancellationException("用户请求暂停任务"))
            Log.d(TAG, "已取消任务 $任务Id 的Job")
        } ?: Log.w(TAG, "未找到任务 $任务Id 的活跃Job")
        
        // 引擎的取消/暂停方法会更新状态到仓库
        Log.d(TAG, "已请求暂停(取消Job)任务: $任务Id")
        // 注意：Ktor基础引擎的取消会触发状态更新为"已取消"，如果需要"已暂停"状态，引擎内部需要区分
        // 或者这里直接更新数据库状态为"已暂停"
        serviceScope.launch{
            val task = 下载仓库.获取下载任务快照(任务Id)
            if(task != null && task.状态 == 下载状态.正在下载){
                下载仓库.更新下载任务(task.copy(状态 = 下载状态.已暂停))
                // 手动更新通知，因为job取消可能不会立即触发状态流的暂停状态
                val notificationId = findNotificationIdForTask(task.id) ?: notificationIdCounter.get()
                updateNotification(notificationId, task.copy(状态 = 下载状态.已暂停), "已暂停")
            }
        }
    }

    /**
     * 取消任务
     * 删除文件等操作应该由 下载管理器 的取消方法负责，并通过引擎实现
     */
    private fun 取消指定任务(任务Id: String) {
        activeDownloadJobs[任务Id]?.let { job ->
            job.cancel(kotlinx.coroutines.CancellationException("用户请求取消任务"))
        }

        // 引擎的取消方法会处理后续，包括文件清理和状态更新
        // 如果引擎的取消逻辑不充分，可以在这里补充，例如：
        serviceScope.launch{
            val task = 下载仓库.获取下载任务快照(任务Id)
            if(task != null){
                下载仓库.更新下载任务(task.copy(状态 = 下载状态.已取消))
            }
        }
    }
    
    private fun triggerNextTaskInQueue(){
        serviceScope.launch {
            Log.d(TAG, "尝试触发下一个等待中的任务")
            val waitingTasks = 下载仓库.获取特定状态任务快照列表(下载状态.等待中.ordinal)
            if (waitingTasks.isNotEmpty()) {
                val nextTask = waitingTasks.first()
                Log.d(TAG, "找到下一个等待中的任务: ${nextTask.id}，将发送到处理队列")
                taskProcessingChannel.send(nextTask.id)
            } else {
                Log.d(TAG, "没有等待中的任务")
            }
        }
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                NOTIFICATION_CHANNEL_ID,
                "下载服务",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "用于显示文件下载进度"
            }
            val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            manager.createNotificationChannel(channel)
            Log.d(TAG, "通知渠道已创建")
        }
    }

    private fun buildNotification(任务: 下载任务, contentTextPrefix: String): NotificationCompat.Builder {
        // TODO: 点击通知的操作，例如打开下载列表界面
        // val pendingIntent = PendingIntent.getActivity(this, 0, Intent(this, YourDownloadListActivity::class.java), PendingIntent.FLAG_IMMUTABLE)

        val progress = if (任务.文件大小 > 0) (任务.已下载大小 * 100 / 任务.文件大小).toInt() else -1 // -1 表示不确定进度
        val contentText = "$contentTextPrefix: ${任务.文件名} (${formatBytes(任务.已下载大小)} / ${if(任务.文件大小 > 0) formatBytes(任务.文件大小) else "未知大小"})"

        return NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
            .setContentTitle("下载任务")
            .setContentText(contentText)
            .setSmallIcon(R.drawable.ic_launcher_foreground) // TODO: 替换为下载图标
            // .setContentIntent(pendingIntent)
            .setOnlyAlertOnce(true)
            .setOngoing(任务.状态 == 下载状态.正在下载) // 正在下载时设为持续性通知
            .setProgress(100, if (progress >=0) progress else 0, progress < 0) // indeterminate if progress < 0
    }

    private fun updateNotification(notificationId: Int, 任务: 下载任务, contentTextPrefix: String) {
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(notificationId, buildNotification(任务, contentTextPrefix).build())
    }

    private fun findNotificationIdForTask(taskId: String): Int? {
        // 简陋实现：实际项目中，应该在启动任务时存储 taskId -> notificationId 的映射
        // 这里只是为了示例，不推荐直接使用
        return activeDownloadJobs.keys.indexOf(taskId) + NOTIFICATION_ID_OFFSET // 非常不准确
    }

    private fun formatBytes(bytes: Long): String {
        if (bytes < 1024) return "$bytes B"
        val kb = bytes / 1024.0
        if (kb < 1024) return String.format("%.1f KB", kb)
        val mb = kb / 1024.0
        if (mb < 1024) return String.format("%.1f MB", mb)
        val gb = mb / 1024.0
        return String.format("%.1f GB", gb)
    }


    override fun onBind(intent: Intent): IBinder = binder

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "服务已销毁")
        taskProcessingChannel.close()
        Log.d(TAG, "任务处理队列已关闭")
        activeDownloadJobs.values.forEach { 
            Log.d(TAG, "取消活跃下载任务Job: ${it}")
            it.cancel("服务销毁") 
        }
        activeDownloadJobs.clear()
        Log.d(TAG, "所有活跃下载任务已取消")
        serviceJob.cancel()
        Log.i(TAG, "所有下载任务已取消，服务资源已释放")
    }

    companion object {
        const val 开始下载 = "cn.mujiankeji.mbrowser.downloader.action.START_DOWNLOAD_TASK_BY_ID"
        const val 暂停下载 = "cn.mujiankeji.mbrowser.downloader.action.PAUSE_DOWNLOAD"
        const val 恢复下载 = "cn.mujiankeji.mbrowser.downloader.action.RESUME_DOWNLOAD"
        const val 取消下载 = "cn.mujiankeji.mbrowser.downloader.action.CANCEL_DOWNLOAD"
        // 移除旧的 ACTION_START_DOWNLOAD，因为现在都通过ID启动

        const val EXTRA_TASK_ID = "extra_task_id"
        // 移除 EXTRA_URL 和 EXTRA_FILENAME，因为任务信息会从仓库获取
        
        private const val MAX_CONCURRENT_DOWNLOADS = 3 // 最大并发下载数
    }
} 